#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include "config.h"
#include "log.h"
#include "pollstat.h"
#include "report.h"
#include "snmpapi.h"

#define DEFAULT_FRUNKD_PID_FILE "frunkd.pid"
#define DEFAULT_FRUNKD_CONF_FILE "frunkd.conf"

static int nReadConfig=0;

static void
SetReadConfig(int sig)
{
  nReadConfig=1;
}

static int
InstallSetReadConfigSigHandler(void)
{
  struct sigaction act;

  act.sa_handler=SetReadConfig;
  sigemptyset(&act.sa_mask);
  act.sa_flags=0;

  return sigaction(SIGHUP, &act, NULL);
}

static int
daemonize(void)
{
  struct rlimit resourceLimit;
  pid_t pid;
  int i;

  if((pid=fork())<0)
  {
    perror("first fork");
    return -1;
  }
  else if(pid!=0)
  {
    /* Parent goes away */
    exit(0);
  }

  /* Child continues */

  /* Close all file descriptors except stderr */
  resourceLimit.rlim_max=0;
  if(getrlimit(RLIMIT_NOFILE, &resourceLimit)<0)
  {
    perror("getrlimit");
    return -1;
  }
  close(0);
  close(1);
  for(i=3; i<resourceLimit.rlim_max; i++)
  {
    close(i);
  }

  /* Start new session */
  if(setsid()<0)
  {
    perror("setsid");
    return -1;
  }

  /* Fork again so that we are not a session leader and */
  /* so we won't acquire a new controlling terminal     */
  if((pid=fork())<0)
  {
    perror("second fork");
    return -1;
  }
  else if(pid!=0)
  {
    /* Parent goes away */
    exit(0);
  }
 
  /* Child continues */

  return 0;
}

static void
usage(const char progname[])
{
  fprintf(stderr, "Usage: %s -c config_file\n", progname);
}

static int
ProcArgs(int argc, char *argv[], char szConfigFile[])
{
  int option;

  szConfigFile[0]='\0';

  while((option=getopt(argc, argv, "c:v"))!=EOF)
  {
    switch(option)
    {
      case 'c':
        if(szConfigFile[0])
        {
          fprintf(stderr,
                  "Last specified config file '%s' will be used.\n", optarg);
        }
        strcpy(szConfigFile, optarg);
        break;
      case 'v':
        fprintf(stdout, "Frunk v%s\n", VERSION_FRUNKD);
        break;
      default:
        return 0;
    }
  }

  if(!szConfigFile[0])
  {
    sprintf(szConfigFile, "%s/%s",
		    DEFAULT_FRUNKD_DIR, DEFAULT_FRUNKD_CONF_FILE);
    fprintf(stderr, "Using default config file '%s'.\n", szConfigFile);
  }

  return 1;
}

static int
WritePIDFile(void)
{
  char buf[32];
  int fd=-1;

  fd=open(DEFAULT_FRUNKD_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644);
  if(fd<0)
  {
    perror("open");
    goto cleanup;
  }
  sprintf(buf, "%d\n", getpid());
  if(write(fd, buf, strlen(buf))<strlen(buf))
  {
    perror("write");
    goto cleanup;
  }

cleanup:
  if(fd>=0)
  {
    close(fd);
  }
  return 0;
}

int
main(int argc, char *argv[])
{
  CONFIG_DATA configData;
  char szConfigFile[_POSIX_PATH_MAX+1];
  int fd;

  /* Process arguments */
  if(!ProcArgs(argc, argv, szConfigFile))
  {
    usage(argv[0]);
    exit(1);
  }

  /* Read config file */
  if(Config_Read(&configData, szConfigFile)<0)
  {
    fprintf(stderr,
            "Failed reading config file '%s'.\nExiting.\n", szConfigFile);
    exit(1);
  }

  /* Daemonize process */
  if(daemonize()<0)
  {
    fprintf(stderr, "Could not daemonize process.\nExiting.\n");
    exit(1);
  }

  /* Install SIGHUP handler to reread config file */
  if(InstallSetReadConfigSigHandler()<0)
  {
    fprintf(stderr, "Could not install SIGHUP handler.\nExiting\n.");
    exit(1);
  }

  /* Set working directory */
  if(chdir(configData.pstrWorkingDirectory)<0)
  {
    fprintf(stderr, "Could not set working directory to '%s'.\nExiting.\n",
            configData.pstrWorkingDirectory);
    exit(1);
  }

  /* Set default umask */
  umask(S_IWGRP | S_IWOTH);

  /* Write PID file */
  if(WritePIDFile()<0)
  {
    fprintf(stderr, "Could not write to PID file '%s'.\nExiting.\n",
            DEFAULT_FRUNKD_PID_FILE);
    exit(1);
  }

  /* Close stderr and open stdin, stdout and stderr to /dev/null */
  close(2);
  fd=open("/dev/null", O_RDWR);
  dup(fd);
  dup(fd);

  for(;;)
  {
    int starttime, deltatime, sleeptime;

    if(nReadConfig)
    {
      CONFIG_DATA configDataNew;

      nReadConfig=0;
      if(Config_Read(&configDataNew, szConfigFile)<0)
      {
        Logf(FRUNK_LOG_ERR, "Failed to reread config file %s.", szConfigFile);
      }
      else
      {
        Config_Consolidate(&configData, &configDataNew);
        Logf(FRUNK_LOG_LOG, "Successfuly reread config file %s.",
			szConfigFile);
      }
    }

    starttime=time(NULL);
    PollStatus(&configData);
    ReportEvents(&configData);
    deltatime=time(NULL)-starttime;
    sleeptime=configData.nSleepInterval-deltatime;
    if(sleeptime>0)
    {
      sleep(sleeptime);
    }
    else
    {
      Logf(FRUNK_LOG_ERR, "Actual polling delay (%d minutes) is larger than "
		"or equal to configured polling interval (%d minutes).",
           deltatime/60, configData.nSleepInterval/60);
    }
  }

  /* Never gets here... */
  return 0;
}

